package com.leap.controller;


import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.leap.entity.*;
import com.leap.entity.dto.ArticleDTO;
import com.leap.entity.dto.ArticleUploadDTO;
import com.leap.entity.vo.ArticleCategoryList;
import com.leap.entity.vo.ArticleVO;
import com.leap.entity.vo.ArticleVOList;
import com.leap.entity.vo.HistoryVO;
import com.leap.service.*;
import com.leap.utils.Const;
import com.leap.utils.JwtUtils;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.web.bind.annotation.*;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import java.util.concurrent.TimeUnit;

@RestController
@RequestMapping("/api/article")
@Tag(name = "文章相关接口服务")
@Slf4j
public class ArticleController {

    @Autowired
    private ArticleService articleService;

    @Autowired
    private MediaService mediaService;

    @Autowired
    private AccountService accountService;

    @Autowired
    private AccountCategoryService accountCategoryService;

    @Autowired
    private HistoryService historyService;

    @Autowired
    private CategoryService categoryService;

    @Autowired
    RedisTemplate redisTemplate;

    @Autowired
    JwtUtils utils;

    /**
     * 查询页面 详细内容
     * @param articleId
     * @return
     */
    @Operation(summary = "文章详细页面接口")
    @GetMapping("/innerList")
    public RestBean<ArticleDTO> innerList(Long articleId,
                                          HttpServletRequest request){
        String headerToken = request.getHeader("Authorization");
        Long accountId = utils.getAccountIdByToken(headerToken);
        ArticleDTO articleDTO = articleService.getInfoByArticleId(articleId);

        String key = Const.ACCOUNT_LIKE + accountId;

        List<Long> articleLikeList = redisTemplate.opsForList().range(key, 0, -1);

        int isLiked = articleLikeList.contains(articleId) ? 1 : 0;

        articleDTO.setIsLiked(isLiked);

        historyService.insertHistory(articleId,headerToken);

        articleService.increseBrowseNum(articleId);

        articleService.refreshArticleSumCache();

        articleService.clearRedisCache();
        
        return RestBean.success(articleDTO);
    }

    /**
     * 展示视频板块和热门板块文章页面信息的获取
     * @param page
     * @return
     */
    @Operation(summary = "首页视频和热门板块文章信息集合接口")
    @GetMapping("/outList")
    public RestBean<ArticleVOList> outList(int page,int isVideo,
                                             HttpServletRequest request){
        String headerToken = request.getHeader("Authorization");
        Long accountId = utils.getAccountIdByToken(headerToken);

        //从Redis中查缓存数据
        String article_key = Const.FIRST_VIDEO_OR_HOT + page + "_" + isVideo + "_" + accountId;

        List<ArticleVO> RedisArticleListVO = redisTemplate.opsForList().range(article_key, 0, -1);
        if (RedisArticleListVO.size() != 0){
            String article_sum = Const.ARTICLE_SUM;
            String video_sum = Const.VIDEO_ARTICLE_SUM;
            Object o = redisTemplate.opsForValue().get(article_sum);
            Object j = redisTemplate.opsForValue().get(video_sum);
            int articleSum =0;
            int videoSum =0;
            if (o != null || j != null){
                articleSum = Integer.parseInt(o.toString());
                videoSum = Integer.parseInt(j.toString());
            }else {
                articleService.refreshArticleSumCache();
                throw new StringIndexOutOfBoundsException();
            }
            ArticleVOList articleVOList = ArticleVOList.builder()
                    .articleVOList(RedisArticleListVO)
                    .articleSum(articleSum)
                    .videoSum(videoSum)
                    .build();
            return RestBean.success(articleVOList);
        }
        //Redis中无缓存数据

        String key = Const.ACCOUNT_LIKE + accountId;

        List<Long> articleLikeList = redisTemplate.opsForList().range(key, 0, -1);

        Page<Article> pageInfo = new Page<>(page, Const.PAGE_SIZE);

        LambdaQueryWrapper<Article> articleQueryWrapper = new LambdaQueryWrapper<>();
        articleQueryWrapper.orderByDesc(Article::getBrowseNum);
        articleQueryWrapper.orderByDesc(Article::getUpdateTime);
        articleQueryWrapper.eq(Article::getIsPublish, 1);
        articleQueryWrapper.eq(Article::getIsVideo,isVideo);

        articleService.page(pageInfo, articleQueryWrapper);

        List<Article> articleList = pageInfo.getRecords();
        if (articleList.size() == 0){
            throw new StringIndexOutOfBoundsException();
        }

        List<ArticleVO> articleListVO = articleList.stream().map(item -> {
            Long articleId = item.getId();
            Media media = mediaService.getCoverById(articleId);

            if (!articleLikeList.isEmpty()){
                return ArticleVO.builder()
                        .articleId(articleId)
                        .title(item.getTitle())
                        .category(item.getCategoryId())
                        .coverUrl(media==null ? null : media.getMediaUrl())
                        .browseNum(item.getBrowseNum())
                        .likeNum(item.getLikeNum())
                        .duration(media==null ? 0L : media.getDuration())
                        .isVideo(item.getIsVideo())
                        .isLiked(articleLikeList.contains(articleId) ? 1 : 0)
                        .isPublish(item.getIsPublish())
                        .updateTime(item.getUpdateTime())
                        .build();
            }else {
                return ArticleVO.builder()
                        .articleId(articleId)
                        .title(item.getTitle())
                        .category(item.getCategoryId())
                        .coverUrl(media==null ? null : media.getMediaUrl())
                        .browseNum(item.getBrowseNum())
                        .likeNum(item.getLikeNum())
                        .duration(media==null ? 0L : media.getDuration())
                        .isVideo(item.getIsVideo())
                        .isLiked(0)
                        .isPublish(item.getIsPublish())
                        .updateTime(item.getUpdateTime())
                        .build();
            }
        }).toList();
        if (!articleListVO.isEmpty()){
            redisTemplate.opsForList().rightPushAll(article_key, articleListVO);
            redisTemplate.expire(article_key, 1, TimeUnit.DAYS);
        }

        String article_sum = Const.ARTICLE_SUM;
        String video_sum = Const.VIDEO_ARTICLE_SUM;
        Object o = redisTemplate.opsForValue().get(article_sum);
        Object j = redisTemplate.opsForValue().get(video_sum);
        int articleSum =0;
        int videoSum =0;
        if (o != null || j != null){
            articleSum = Integer.parseInt(o.toString());
            videoSum = Integer.parseInt(j.toString());
        }else {
            articleService.refreshArticleSumCache();
            throw new StringIndexOutOfBoundsException();
        }
        ArticleVOList articleVOList = ArticleVOList.builder()
                .articleVOList(articleListVO)
                .articleSum(articleSum)
                .videoSum(videoSum)
                .build();
        return RestBean.success(articleVOList);
    }


    /**
     * 展示最新板块文章页面信息的获取
     * @param page
     * @return
     */
    @Operation(summary = "首页最新板块文章信息集合接口")
    @GetMapping("/outListNew")
    public RestBean<ArticleVOList> outListNew(int page,HttpServletRequest request){

        String headerToken = request.getHeader("Authorization");
        Long accountId = utils.getAccountIdByToken(headerToken);
        //从Redis中查缓存数据
        String article_key = Const.FIRST_NEW + page + "_" + accountId;

        List<ArticleVO> RedisArticleListVO = redisTemplate.opsForList().range(article_key, 0, -1);
        if (RedisArticleListVO.size() != 0){
            String article_sum = Const.ARTICLE_SUM;
            String video_sum = Const.VIDEO_ARTICLE_SUM;
            Object o = redisTemplate.opsForValue().get(article_sum);
            Object j = redisTemplate.opsForValue().get(video_sum);
            int articleSum =0;
            int videoSum =0;
            if (o != null || j != null){
                articleSum = Integer.parseInt(o.toString());
                videoSum = Integer.parseInt(j.toString());
            }else {
                articleService.refreshArticleSumCache();
                throw new StringIndexOutOfBoundsException();
            }
            ArticleVOList articleVOList = ArticleVOList.builder()
                    .articleVOList(RedisArticleListVO)
                    .articleSum(articleSum)
                    .videoSum(videoSum)
                    .build();
            return RestBean.success(articleVOList);
        }
        //Redis中无缓存数据

        String key = Const.ACCOUNT_LIKE + accountId;

        List<Long> articleLikeList = redisTemplate.opsForList().range(key, 0, -1);

        Page<Article> pageInfo = new Page<>(page, Const.PAGE_SIZE);

        LambdaQueryWrapper<Article> articleQueryWrapper = new LambdaQueryWrapper<>();
        articleQueryWrapper.orderByDesc(Article::getUpdateTime);
        articleQueryWrapper.eq(Article::getIsPublish, 1);
        articleQueryWrapper.eq(Article::getIsVideo,0);

        articleService.page(pageInfo, articleQueryWrapper);

        List<Article> articleList = pageInfo.getRecords();
        if (articleList.size() == 0){
            throw new StringIndexOutOfBoundsException();
        }

        List<ArticleVO> articleListVO = articleList.stream().map(item -> {
            Long articleId = item.getId();
            Media media = mediaService.getCoverById(articleId);
            if (!articleLikeList.isEmpty()){
                return ArticleVO.builder()
                        .articleId(articleId)
                        .title(item.getTitle())
                        .category(item.getCategoryId())
                        .coverUrl(media==null ? null : media.getMediaUrl())
                        .browseNum(item.getBrowseNum())
                        .likeNum(item.getLikeNum())
                        .duration(media==null ? 0L : media.getDuration())
                        .isVideo(item.getIsVideo())
                        .isLiked(articleLikeList.contains(articleId) ? 1 : 0)
                        .isPublish(item.getIsPublish())
                        .updateTime(item.getUpdateTime())
                        .build();
            }else {
                return ArticleVO.builder()
                        .articleId(articleId)
                        .title(item.getTitle())
                        .category(item.getCategoryId())
                        .coverUrl(media==null ? null : media.getMediaUrl())
                        .browseNum(item.getBrowseNum())
                        .likeNum(item.getLikeNum())
                        .duration(media==null ? 0L : media.getDuration())
                        .isVideo(item.getIsVideo())
                        .isLiked(0)
                        .isPublish(item.getIsPublish())
                        .updateTime(item.getUpdateTime())
                        .build();
            }


        }).toList();
        if (!articleListVO.isEmpty()){
            redisTemplate.opsForList().rightPushAll(article_key, articleListVO);
            redisTemplate.expire(article_key, 1, TimeUnit.DAYS);
        }

        String article_sum = Const.ARTICLE_SUM;
        String video_sum = Const.VIDEO_ARTICLE_SUM;
        Object o = redisTemplate.opsForValue().get(article_sum);
        Object j = redisTemplate.opsForValue().get(video_sum);
        int articleSum =0;
        int videoSum =0;
        if (o != null || j != null){
            articleSum = Integer.parseInt(o.toString());
            videoSum = Integer.parseInt(j.toString());
        }else {
            articleService.refreshArticleSumCache();
            throw new StringIndexOutOfBoundsException();
        }
        ArticleVOList articleVOList = ArticleVOList.builder()
                .articleVOList(articleListVO)
                .articleSum(articleSum)
                .videoSum(videoSum)
                .build();
        return RestBean.success(articleVOList);
    }

    /**
     * 首页展示文章页面信息的获取
     * @param page
     * @return
     */
    @Operation(summary = "首页文章信息集合接口")
    @GetMapping("/firstList")
    public RestBean<ArticleVOList> firstList(int page,
                                               HttpServletRequest request){

        String headerToken = request.getHeader("Authorization");
        Long accountId = utils.getAccountIdByToken(headerToken);
        //从Redis中查缓存数据
        String article_key = Const.FIRST_PAGE + page + "_" + accountId;

        List<ArticleVO> RedisArticleListVO = redisTemplate.opsForList().range(article_key, 0, -1);
        if (RedisArticleListVO.size() != 0){
            String article_sum = Const.ARTICLE_SUM;
            String video_sum = Const.VIDEO_ARTICLE_SUM;
            Object o = redisTemplate.opsForValue().get(article_sum);
            Object j = redisTemplate.opsForValue().get(video_sum);
            int articleSum =0;
            int videoSum =0;
            if (o != null || j != null){
                articleSum = Integer.parseInt(o.toString());
                videoSum = Integer.parseInt(j.toString());
            }else {
                articleService.refreshArticleSumCache();
                throw new StringIndexOutOfBoundsException();
            }
            ArticleVOList articleVOList = ArticleVOList.builder()
                    .articleVOList(RedisArticleListVO)
                    .articleSum(articleSum)
                    .videoSum(videoSum)
                    .build();
            return RestBean.success(articleVOList);
        }
        //Redis中无缓存数据

        String key = Const.ACCOUNT_LIKE + accountId;

        List<Long> articleLikeList = redisTemplate.opsForList().range(key, 0, -1);

        Page<Article> pageInfo = new Page<>(page, Const.FIRST_PAGE_SIZE);

        Random random = new Random();
        int sum = Const.FIRST_PAGE_SIZE;
        int divide = Const.FIRST_PAGE_SIZE / Const.PAGE_SIZE;
        int index = 0;

        LambdaQueryWrapper<Article> articleQueryWrapper = new LambdaQueryWrapper<>();
        articleQueryWrapper.orderByDesc(Article::getBrowseNum);
        articleQueryWrapper.orderByDesc(Article::getUpdateTime);
        articleQueryWrapper.eq(Article::getIsPublish, 1);
        articleQueryWrapper.eq(Article::getIsVideo,0);

        articleService.page(pageInfo, articleQueryWrapper);

        List<Article> articleList = pageInfo.getRecords();
        if (articleList.size() > Const.FIRST_PAGE_SIZE || articleList.size() == 0){
            throw new StringIndexOutOfBoundsException();
        }

        List<ArticleVO> articleListVO = new ArrayList<>();
        int size = articleList.size();

        while (index <= sum - divide){
            int randomNum = random.nextInt(divide) + index;
            if (randomNum>= sum){
                randomNum = sum - 1;
            }
            if (randomNum >= size){
                throw new StringIndexOutOfBoundsException();
            }
            Article item = articleList.get(randomNum);
            Long articleId = item.getId();
            Media media = mediaService.getCoverById(articleId);
            ArticleVO articleVO = null;
            if (!articleLikeList.isEmpty()) {
                articleVO = ArticleVO.builder()
                        .articleId(articleId)
                        .title(item.getTitle())
                        .category(item.getCategoryId())
                        .coverUrl(media==null ? null : media.getMediaUrl())
                        .browseNum(item.getBrowseNum())
                        .likeNum(item.getLikeNum())
                        .duration(media==null ? 0L : media.getDuration())
                        .isVideo(item.getIsVideo())
                        .isLiked(articleLikeList.contains(articleId) ? 1 : 0)
                        .isPublish(item.getIsPublish())
                        .updateTime(item.getUpdateTime())
                        .build();
            }else {
                articleVO = ArticleVO.builder()
                        .articleId(articleId)
                        .title(item.getTitle())
                        .category(item.getCategoryId())
                        .coverUrl(media==null ? null : media.getMediaUrl())
                        .browseNum(item.getBrowseNum())
                        .likeNum(item.getLikeNum())
                        .duration(0L)
                        .isVideo(item.getIsVideo())
                        .isLiked(0)
                        .isPublish(item.getIsPublish())
                        .updateTime(item.getUpdateTime())
                        .build();
            }
            articleListVO.add(articleVO);
            index += divide;
        }

        redisTemplate.opsForList().rightPushAll(article_key, articleListVO);
        redisTemplate.expire(article_key, 1, TimeUnit.DAYS);

        String article_sum = Const.ARTICLE_SUM;
        String video_sum = Const.VIDEO_ARTICLE_SUM;
        Object o = redisTemplate.opsForValue().get(article_sum);
        Object j = redisTemplate.opsForValue().get(video_sum);
        int articleSum =0;
        int videoSum =0;
        if (o != null || j != null){
            articleSum = Integer.parseInt(o.toString());
            videoSum = Integer.parseInt(j.toString());
        }else {
            articleService.refreshArticleSumCache();
            throw new StringIndexOutOfBoundsException();
        }
        ArticleVOList articleVOList = ArticleVOList.builder()
                .articleVOList(articleListVO)
                .articleSum(articleSum)
                .videoSum(videoSum)
                .build();
        return RestBean.success(articleVOList);
    }

    /**
     * 通过分类获取热门文章集合接口
     * @param page
     * @param category
     * @return
     */
    @Operation(summary = "通过分类获取热门文章集合接口")
    @GetMapping("/listByCategory")
    public RestBean<ArticleCategoryList> listByCategory(int page, @RequestParam(defaultValue = "0") int category,
                                                        HttpServletRequest request){
        String headerToken = request.getHeader("Authorization");
        Long accountId = utils.getAccountIdByToken(headerToken);

        //从Redis中查缓存数据
        if (category > 4 ){
            log.info("category_id: {}", category);
            throw new StringIndexOutOfBoundsException("未知错误 请稍后重试");
        }
        //加载文章统计数
        int articleSum = 0;
        int videoSum = 0;
        int c1_articleSum = 0;
        int c2_articleSum = 0;
        int c3_articleSum = 0;
        int c4_articleSum = 0;

        int c1_videoSum = 0;
        int c2_videoSum = 0;
        int c3_videoSum = 0;
        int c4_videoSum = 0;

        String article = Const.ARTICLE_SUM;
        String video = Const.VIDEO_ARTICLE_SUM;

        String ca1 = Const.CATEGORY_1;
        String ca2 = Const.CATEGORY_2;
        String ca3 = Const.CATEGORY_3;
        String ca4 = Const.CATEGORY_4;
        String cv1 = Const.CATEGORY_1_VIDEO;
        String cv2 = Const.CATEGORY_2_VIDEO;
        String cv3 = Const.CATEGORY_3_VIDEO;
        String cv4 = Const.CATEGORY_4_VIDEO;

        Object a = redisTemplate.opsForValue().get(article);
        Object v = redisTemplate.opsForValue().get(video);

        Object cas1 = redisTemplate.opsForValue().get(ca1);
        Object cas2 = redisTemplate.opsForValue().get(ca2);
        Object cas3 = redisTemplate.opsForValue().get(ca3);
        Object cas4 = redisTemplate.opsForValue().get(ca4);

        Object cvs1 = redisTemplate.opsForValue().get(cv1);
        Object cvs2 = redisTemplate.opsForValue().get(cv2);
        Object cvs3 = redisTemplate.opsForValue().get(cv3);
        Object cvs4 = redisTemplate.opsForValue().get(cv4);

        if (a != null || v != null || cas1 != null || cas2 != null || cas3 != null || cas4 != null
                || cvs1 != null || cvs2 != null || cvs3 != null || cvs4 != null){
            articleSum = a == null ? 0 : Integer.parseInt(a.toString());
            videoSum = v == null ? 0 : Integer.parseInt(v.toString());

            c1_articleSum = cas1 == null ? 0 : Integer.parseInt(cas1.toString());
            c2_articleSum = cas2 == null ? 0 : Integer.parseInt(cas2.toString());
            c3_articleSum = cas3 == null ? 0 : Integer.parseInt(cas3.toString());
            c4_articleSum = cas4 == null ? 0 : Integer.parseInt(cas4.toString());

            c1_videoSum = cvs1 == null ? 0 : Integer.parseInt(cvs1.toString());
            c2_videoSum = cvs2 == null ? 0 : Integer.parseInt(cvs2.toString());
            c3_videoSum = cvs3 == null ? 0 : Integer.parseInt(cvs3.toString());
            c4_videoSum = cvs4 == null ? 0 : Integer.parseInt(cvs4.toString());
        }else {
            articleService.refreshArticleSumCache();
            throw new StringIndexOutOfBoundsException("未知错误 请稍后重试");
        }

        String article_key = Const.CATEGORY_LIST + page + "_" + category + "_" + accountId;

        List<ArticleVO> RedisArticleListVO = redisTemplate.opsForList().range(article_key, 0, -1);
        if (RedisArticleListVO.size() != 0){
            ArticleCategoryList articleCategoryList = ArticleCategoryList.builder()
                    .articleVOList(RedisArticleListVO)
                    .articleSum(articleSum)
                    .videoSum(videoSum)
                    .category1(c1_articleSum)
                    .category2(c2_articleSum)
                    .category3(c3_articleSum)
                    .category4(c4_articleSum)
                    .category1_video(c1_videoSum)
                    .category2_video(c2_videoSum)
                    .category3_video(c3_videoSum)
                    .category4_video(c4_videoSum)
                    .build();
            return RestBean.success(articleCategoryList);
        }

        //Redis中无缓存数据

        String key = Const.ACCOUNT_LIKE + accountId;

        List<Long> articleLikeList = redisTemplate.opsForList().range(key, 0, -1);

        Page<Article> pageInfo = new Page<>(page, Const.PAGE_SIZE);

        LambdaQueryWrapper<Article> articleQueryWrapper = new LambdaQueryWrapper<>();
        articleQueryWrapper.orderByDesc(Article::getUpdateTime);
        articleQueryWrapper.eq(Article::getIsPublish, 1);
        if (category != 0 ){
            articleQueryWrapper.eq(Article::getCategoryId, category);
        }
        articleQueryWrapper.eq(Article::getIsVideo,0);

        articleService.page(pageInfo, articleQueryWrapper);

        List<Article> articleList = pageInfo.getRecords();
        if (articleList.size() == 0){
            throw new StringIndexOutOfBoundsException();
        }

        List<ArticleVO> articleListVO = articleList.stream().map(item -> {
            Long articleId = item.getId();
            Media media = mediaService.getCoverById(articleId);
            if (!articleLikeList.isEmpty()){
                return ArticleVO.builder()
                        .articleId(articleId)
                        .title(item.getTitle())
                        .category(item.getCategoryId())
                        .coverUrl(media==null ? null : media.getMediaUrl())
                        .browseNum(item.getBrowseNum())
                        .likeNum(item.getLikeNum())
                        .duration(media==null ? 0L : media.getDuration())
                        .isVideo(item.getIsVideo())
                        .isLiked(articleLikeList.contains(articleId) ? 1 : 0)
                        .isPublish(item.getIsPublish())
                        .updateTime(item.getUpdateTime())
                        .build();
            }else {
                return ArticleVO.builder()
                        .articleId(articleId)
                        .title(item.getTitle())
                        .category(item.getCategoryId())
                        .coverUrl(media==null ? null : media.getMediaUrl())
                        .browseNum(item.getBrowseNum())
                        .likeNum(item.getLikeNum())
                        .duration(media==null ? 0L : media.getDuration())
                        .isVideo(item.getIsVideo())
                        .isLiked(0)
                        .isPublish(item.getIsPublish())
                        .updateTime(item.getUpdateTime())
                        .build();
            }

        }).toList();
        if (!articleListVO.isEmpty()){
            redisTemplate.opsForList().rightPushAll(article_key, articleListVO);
            redisTemplate.expire(article_key, 1, TimeUnit.DAYS);
        }

        ArticleCategoryList articleCategoryList = ArticleCategoryList.builder()
                .articleVOList(articleListVO)
                .articleSum(articleSum)
                .videoSum(videoSum)
                .category1(c1_articleSum)
                .category2(c2_articleSum)
                .category3(c3_articleSum)
                .category4(c4_articleSum)
                .category1_video(c1_videoSum)
                .category2_video(c2_videoSum)
                .category3_video(c3_videoSum)
                .category4_video(c4_videoSum)
                .build();

        return RestBean.success(articleCategoryList);

    }

    /**
     * 获取用户浏览历史文章集合
     * @param isVideo
     * @param request
     * @return
     */
    @Operation(summary = "获取用户浏览历史文章集合接口")
    @GetMapping("/history")
    public RestBean<List<ArticleVO>> historyList(int isVideo,
                                                 HttpServletRequest request){
        String headerToken = request.getHeader("Authorization");

        List<ArticleVO> articleVOList = articleService.selectByToken(headerToken,isVideo);

        return RestBean.success(articleVOList);
    }

    /**
     * 按标题搜索
     * @param title
     * @param category
     * @return
     */
    @Operation(summary = "文章模糊搜索接口")
    @GetMapping("/search")
    public RestBean<List<ArticleVO>> selectByTitle(String title,int page,
                                                    @RequestParam(defaultValue = "0") int category,
                                                   HttpServletRequest request){
        //添加到历史记录表中
        String headerToken = request.getHeader("Authorization");
        articleService.saveHistory(title,headerToken);
        Long accountId = utils.getAccountIdByToken(headerToken);

        List<ArticleVO> articleVOList = articleService.selectByTitile(title,category,accountId,page);

        return RestBean.success(articleVOList);
    }

    /**
     * 发表文章
     * @param articleDTO
     * @return
     */
    @Operation(summary = "文章发表接口")
    @PostMapping("/save")
    public RestBean<ArticleUploadDTO> save(@RequestBody ArticleUploadDTO articleDTO){
        log.info("ArticleUploadDTO_saveIn: {}", articleDTO);
        ArticleUploadDTO articleUploadDTO = articleService.saveDTO(articleDTO);
        //刷新文章集合总数
        articleService.refreshArticleSumCache();

        articleService.clearRedisCache();

        return RestBean.success(articleUploadDTO);
    }

    /**
     * 获取修改文章界面的文章数据
     * @param articleId
     * @return
     */
    @Operation(summary = "文章更改界面获取当前文章数据接口")

    @GetMapping("/uploadInfo")
    public RestBean<ArticleUploadDTO> getUploadDTO(Long articleId){

        ArticleUploadDTO articleUploadDTO = articleService.getUploadDTOByArticleId(articleId);

        return RestBean.success(articleUploadDTO);
    }

    /**
     * 更新文章内容
     * @param articleDTO
     * @return
     */
    @Operation(summary = "文章更新接口")
    @PutMapping("/update")
    public RestBean<String> update(@RequestBody ArticleUploadDTO articleDTO){
        log.info("ArticleUploadDTO_updateIn: {}", articleDTO);

        articleService.updateDTO(articleDTO);

        articleService.refreshArticleSumCache();

        articleService.clearRedisCache();

        return RestBean.success("修改成功");
    }

    /**
     * 删除文章
     * @param articleId
     * @return
     */
    @Operation(summary = "文章删除接口")
    @DeleteMapping()
    public RestBean<String> delete(@RequestParam("id") Long articleId){

        articleService.deleteDTO(articleId);

        articleService.refreshArticleSumCache();

        articleService.clearRedisCache();

        articleService.deleteAllInteract(articleId);

        return RestBean.success("删除成功");
    }

    /**
     * 获取用户历史记录
     * @param request
     * @return
     */
    @Operation(summary = "用户搜索记录获取接口")
    @GetMapping("/searchHistory")
    public RestBean<List<HistoryVO>> getHistory(HttpServletRequest request){
        String headerToken = request.getHeader("Authorization");
        List<HistoryVO> historyVOList = articleService.getHistory(headerToken);

        return RestBean.success(historyVOList);
    }

    /**
     * 关闭页面时统计浏览次数
     * @param articleId
     * @param request
     * @return
     */
    @Operation(summary = "关闭页面详细时自增用户浏览该分类次数接口")
    @PostMapping("/quitPage")
    public RestBean<String> recordCategoryView(Long articleId,
                                             HttpServletRequest request){
        String headerToken = request.getHeader("Authorization");
        articleService.countCategoryView(articleId,headerToken);

        return RestBean.success("");
    }

    /**
     * 分类界面获取文章集合 根据用户浏览每种分类的次数排序
     * @param request
     * @return
     */
    @Operation(summary = "根据用户浏览每种分类的次数排序获取文章集合接口")
    @GetMapping("/categoryAllList")
    public RestBean<ArticleVOList> getCategoryAllList(int page,HttpServletRequest request){
        String headerToken = request.getHeader("Authorization");
        Long accountId = utils.getAccountIdByToken(headerToken);
        List<AccountCategoryView> categoryViewList = accountCategoryService.getCategoryViewByAccountId(accountId);
        log.info("categoryViewList: {}",categoryViewList);
        if (categoryViewList.isEmpty()){
            return this.outList(page, 0,request);
        }
        //获取当前用户收藏文章的id
        String Like_key = Const.ACCOUNT_LIKE + accountId;
        List<Long> articleLikeList = redisTemplate.opsForList().range(Like_key, 0, -1);

        //统计总权重
        int totalWeights = 0;
        //获取权重比集合 cumulativeWeights
        List<Integer> cumulativeWeights = new ArrayList<>();
        for (int i = 0; i < categoryViewList.size(); i++) {
            totalWeights += categoryViewList.get(i).getViewCount();
            if (i == 0 ){
                cumulativeWeights.add(categoryViewList.get(0).getViewCount());
            }else {
                cumulativeWeights.add(cumulativeWeights.get(i-1) +
                        categoryViewList.get(i).getViewCount());
            }
        }
        String key = Const.RECORD_CATEGORY_CHOOSE_NUM + accountId;
        List<ArticleVO> articleVOList = new ArrayList<>();

        for (int i = 0; i < Const.PAGE_SIZE; i++) {
            int randomNum = new Random().nextInt(totalWeights);
            int chooseNum = 0;
            int categoryId = 0;
            //获取某一分类文章已查询的数量chooseNum
            for (int j = 0; j < cumulativeWeights.size(); j++) {
                if (randomNum <= cumulativeWeights.get(j)) {
                    categoryId = categoryViewList.get(j).getCategoryId();
                    log.info("categoryId: {}",categoryId);
                    if (redisTemplate.opsForHash().get(key,Integer.toString(categoryId)) == null){
                        chooseNum = 0;
                    }else {
                        chooseNum = Integer.parseInt(redisTemplate.opsForHash().get(key, Integer.toString(categoryId)).toString());
                    }
                    break;
                }
            }
            Page<Article> pageInfo = new Page<>(1+chooseNum,1);
            LambdaQueryWrapper<Article> articleLambdaQueryWrapper = new LambdaQueryWrapper<>();
            articleLambdaQueryWrapper.orderByDesc(Article::getBrowseNum);
            articleLambdaQueryWrapper.orderByDesc(Article::getUpdateTime);
            articleLambdaQueryWrapper.eq(Article::getCategoryId,categoryId);
            articleLambdaQueryWrapper.eq(Article::getIsPublish, 1);

            articleService.page(pageInfo, articleLambdaQueryWrapper);
            List<Article> records = pageInfo.getRecords();

            if (records.size() == 0){
                throw new StringIndexOutOfBoundsException();
            }

            Article one = null;
            //判断是否获取该分类文章超出数量
            if(records.size()==0){
                pageInfo = new Page<>(1,1);
                articleService.page(pageInfo, articleLambdaQueryWrapper);
                List<Article> oneRecord = pageInfo.getRecords();
                one = oneRecord.get(0);
                redisTemplate.opsForHash().put(key,Integer.toString(categoryId),1);
            }else{
                one = records.get(0);
                redisTemplate.opsForHash().increment(key,Integer.toString(categoryId),1);
            }
            Media media = mediaService.getCoverById(one.getId());
            ArticleVO articleVO = null;
            if (!articleLikeList.isEmpty()){
                articleVO = ArticleVO.builder()
                        .articleId(one.getId())
                        .title(one.getTitle())
                        .author(one.getAuthorName())
                        .category(one.getCategoryId())
                        .coverUrl(media==null ? null : media.getMediaUrl())
                        .browseNum(one.getBrowseNum())
                        .likeNum(one.getLikeNum())
                        .duration(media==null ? 0L : media.getDuration())
                        .isVideo(one.getIsVideo())
                        .isLiked(articleLikeList.contains(one.getId()) ? 1 : 0)
                        .isPublish(one.getIsPublish())
                        .updateTime(one.getUpdateTime())
                        .build();
            }else{
                articleVO = ArticleVO.builder()
                        .articleId(one.getId())
                        .title(one.getTitle())
                        .author(one.getAuthorName())
                        .category(one.getCategoryId())
                        .coverUrl(media==null ? null : media.getMediaUrl())
                        .browseNum(one.getBrowseNum())
                        .likeNum(one.getLikeNum())
                        .duration(media==null ? 0L : media.getDuration())
                        .isVideo(one.getIsVideo())
                        .isLiked(0)
                        .isPublish(one.getIsPublish())
                        .updateTime(one.getUpdateTime())
                        .build();
            }

            articleVOList.add(articleVO);
        }

        redisTemplate.expire(key, 1, TimeUnit.HOURS);

        String article_sum = Const.ARTICLE_SUM;
        String video_sum = Const.VIDEO_ARTICLE_SUM;
        Object o = redisTemplate.opsForValue().get(article_sum);
        Object j = redisTemplate.opsForValue().get(video_sum);
        int articleSum =0;
        int videoSum =0;
        if (o != null || j != null){
            articleSum = Integer.parseInt(o.toString());
            videoSum = Integer.parseInt(j.toString());
        }else {
            articleService.refreshArticleSumCache();
            throw new StringIndexOutOfBoundsException();
        }
        ArticleVOList list = ArticleVOList.builder()
                .articleVOList(articleVOList)
                .articleSum(articleSum)
                .videoSum(videoSum)
                .build();
        return RestBean.success(list);
    }


    /**
     * 用户收藏文章集合
     * @param request
     * @return
     */
    @Operation(summary = "用户收藏文章集合接口")
    @GetMapping("/likeList")
    public RestBean<List<ArticleVO>> getLikeList(int isVideo,
                                                 HttpServletRequest request){

        String headerToken = request.getHeader("Authorization");
        Long accountId = utils.getAccountIdByToken(headerToken);

        String key = Const.ACCOUNT_LIKE + accountId;

        List<Long> articleLikeList = redisTemplate.opsForList().range(key, 0, -1);
        log.info("articleLikeList: {}",articleLikeList);
        if (articleLikeList.isEmpty()){
            accountService.loadLikeListCache(accountId);
            throw new UsernameNotFoundException("未知错误 请稍后重试");
        }

        LambdaQueryWrapper<Article> articleLambdaQueryWrapper = new LambdaQueryWrapper<>();
        articleLambdaQueryWrapper.in(Article::getId, articleLikeList);
        articleLambdaQueryWrapper.eq(Article::getIsVideo, isVideo);
        articleLambdaQueryWrapper.eq(Article::getIsPublish, 1);

        List<Article> articleList = articleService.list(articleLambdaQueryWrapper);

        if (articleList.size() == 0){
            throw new StringIndexOutOfBoundsException();
        }

        List<ArticleVO> articleVOList = articleList.stream().map(item -> {
            Long articleId = item.getId();
            Media media = mediaService.getCoverById(articleId);
            return ArticleVO.builder()
                    .articleId(articleId)
                    .title(item.getTitle())
                    .category(item.getCategoryId())
                    .coverUrl(media==null ? null : media.getMediaUrl())
                    .browseNum(item.getBrowseNum())
                    .likeNum(item.getLikeNum())
                    .duration(media==null ? 0L : media.getDuration())
                    .isVideo(item.getIsVideo())
                    .isLiked(articleLikeList.contains(articleId) ? 1 : 0)
                    .isPublish(item.getIsPublish())
                    .updateTime(item.getUpdateTime())
                    .build();
        }).toList();

        return RestBean.success(articleVOList);
    }

    /**
     * 文章类型名称获取接口
     * @param categoryId
     * @return
     */
    @Operation(summary = "文章类型名称集合接口")
    @GetMapping("/getCategoryName")
    public RestBean<Category> getCategory(int categoryId){
        String key = Const.CATEGORY_NAME;
        List<Category> categoryNames = redisTemplate.opsForList().range(key, 0, -1)
                .stream().map(item -> JSON.parseObject(JSON.toJSONString(item), Category.class)).toList();
        if (categoryNames.isEmpty()){
            categoryService.loadCategoryName();
            return RestBean.failure(400, "未知错误 请稍后重试");
        }

        for (Category category : categoryNames){
            if (category.getId() == categoryId){
                return RestBean.success(category);
            }
        }
        return RestBean.failure(403, "未知分类Id");

    }










}
